﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Drawing;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.Core;
using Noris.Schedule.Support.DataFace;

using Noris.Schedule.Planning.DataFace;
using Noris.Schedule.Planning.ProcessData;            // To kvůli dolní části, kde je namespace Noris.Schedule.Planning.DataFace

// Tento soubor obsahuje základní slovníky a indexy pro práci s daty o právě probíhající výrobě (výrobní příkazy, perace, komponenty, zdroje)
namespace Noris.Schedule.Planning.ProcessData
{
	// ProductDataCls : zásobník dat o výrobních příkazech, z tohoto zásobníku lze čerpat veškeré informace
	/// <summary>
	/// ProductDataCls : zásobník dat o výrobních příkazech, z tohoto zásobníku lze čerpat veškeré informace.
	/// Součástí této třídy nejsou definice datových vět (dataface pro Noris třídy), ty jsou samostatné v namespace Aps.DataFace
	/// </summary>
	internal class ProductDataCls : PlanningComponentCls, ITechnologyLibrary, IDisposable
	{
		#region KONSTRUKCE
		/// <summary>
		/// Konstruktor, nenačítá data
		/// </summary>
		public ProductDataCls(PlanningComponentCls parent)
			: base(parent)
		{
			// base konstruktor uloží předaný objekt a vyvolá CreateData()
		}
		/// <summary>
		/// Reference na objekt Parent = základna plánovacího procesu
		/// </summary>
		internal PlanningDataCls ParentPlanning { get { return this.GetParent<PlanningDataCls>(); } }
        /// <summary>
        /// Auditor: obsahuje informace a chyby aktuální verze plánu
        /// </summary>
        internal AuditorCls Auditor { get { return this.ParentPlanning.Auditor; } }
        /// <summary>
		/// Vytvoří interní prázdné struktury, nenačítá data
		/// </summary>
		protected override void DataCreate()
		{
			base.DataCreate();

			this._StandardMfrOperationsDict = new Dictionary<int, StandardMfrOperationsCls>();

			this._ProductOrderDict = new Dictionary<int, ProductOrderCls>();
			this._ProductOrderOperationDict = new Dictionary<int, ProductOrderOperationCls>();
			this._ProductOrderStructureDict = new Dictionary<int, ProductOrderStructureCls>();
			this._ProductOrderResourceAllocDict = new Dictionary<int, ProductOrderResourceAllocCls>();

			this._PoOperationIndexFind = new IndexDictCls<ProductOrderOperationFindProductOrderCls, ProductOrderOperationCls>(IndexType.MultiRows);
			this._PoStructureIndexFind = new IndexDictCls<ProductOrderStructureFindOperationPoCls, ProductOrderStructureCls>(IndexType.MultiRows);
			this._PoResourceIndexFind = new IndexDictCls<ProductOrderResourceAllocFindProductOrderOperationCls, ProductOrderResourceAllocCls>(IndexType.MultiRows);

			this._ProductOrderDbDict = new Dictionary<int,ProductOrderCls>();
			this._ProductOrderOperationDbDict = new Dictionary<int,ProductOrderOperationCls>();
			this._ProductOrderStructureDbDict = new Dictionary<int,ProductOrderStructureCls>();
			this._ProductOrderResourceAllocDbDict = new Dictionary<int,ProductOrderResourceAllocCls>();

			this._PoOperationIndexDbFind = new IndexDictCls<ProductOrderOperationFindProductOrderCls, ProductOrderOperationCls>(IndexType.MultiRows);
			this._PoStructureIndexDbFind = new IndexDictCls<ProductOrderStructureFindOperationPoCls, ProductOrderStructureCls>(IndexType.MultiRows);
			this._PoResourceIndexDbFind = new IndexDictCls<ProductOrderResourceAllocFindProductOrderOperationCls, ProductOrderResourceAllocCls>(IndexType.MultiRows);
			
			this._Status = new ProductDataStatusCls();
		}
		public void Dispose()
		{
			if (this._StandardMfrOperationsDict != null) this._StandardMfrOperationsDict.Clear();

			if (this._ProductOrderDict != null) this._ProductOrderDict.Clear();
			if (this._ProductOrderOperationDict != null) this._ProductOrderOperationDict.Clear();
			if (this._ProductOrderStructureDict != null) this._ProductOrderStructureDict.Clear();
			if (this._ProductOrderResourceAllocDict != null) this._ProductOrderResourceAllocDict.Clear();

			if (this._PoOperationIndexFind != null) this._PoOperationIndexFind.Clear();
			if (this._PoStructureIndexFind != null) this._PoStructureIndexFind.Clear();
			if (this._PoResourceIndexFind != null) this._PoResourceIndexFind.Clear();

			if (this._ProductOrderDbDict != null) this._ProductOrderDbDict.Clear();
			if (this._ProductOrderOperationDbDict != null) this._ProductOrderOperationDbDict.Clear();
			if (this._ProductOrderStructureDbDict != null) this._ProductOrderStructureDbDict.Clear();
			if (this._ProductOrderResourceAllocDbDict != null) this._ProductOrderResourceAllocDbDict.Clear();

			if (this._PoOperationIndexDbFind != null) this._PoOperationIndexDbFind.Clear();
			if (this._PoStructureIndexDbFind != null) this._PoStructureIndexDbFind.Clear();
			if (this._PoResourceIndexDbFind != null) this._PoResourceIndexDbFind.Clear();
		}
		private Dictionary<int, StandardMfrOperationsCls> _StandardMfrOperationsDict;

		// Základní balík dat = výrobní příkazy, které vstupují do plánu:
		private Dictionary<int, ProductOrderCls> _ProductOrderDict;
		private Dictionary<int, ProductOrderOperationCls> _ProductOrderOperationDict;
		private Dictionary<int, ProductOrderStructureCls> _ProductOrderStructureDict;
		private Dictionary<int, ProductOrderResourceAllocCls> _ProductOrderResourceAllocDict;

		private IndexDictCls<ProductOrderOperationFindProductOrderCls, ProductOrderOperationCls> _PoOperationIndexFind;
		private IndexDictCls<ProductOrderStructureFindOperationPoCls, ProductOrderStructureCls> _PoStructureIndexFind;
		private IndexDictCls<ProductOrderResourceAllocFindProductOrderOperationCls, ProductOrderResourceAllocCls> _PoResourceIndexFind;

		// Rozšiřující balík dat = výrobní příkazy, které do nového plánu nevstupují, ale program si je donačetl z databáze:
		private Dictionary<int, ProductOrderCls> _ProductOrderDbDict;
		private Dictionary<int, ProductOrderOperationCls> _ProductOrderOperationDbDict;
		private Dictionary<int, ProductOrderStructureCls> _ProductOrderStructureDbDict;
		private Dictionary<int, ProductOrderResourceAllocCls> _ProductOrderResourceAllocDbDict;

		private IndexDictCls<ProductOrderOperationFindProductOrderCls, ProductOrderOperationCls> _PoOperationIndexDbFind;
		private IndexDictCls<ProductOrderStructureFindOperationPoCls, ProductOrderStructureCls> _PoStructureIndexDbFind;
		private IndexDictCls<ProductOrderResourceAllocFindProductOrderOperationCls, ProductOrderResourceAllocCls> _PoResourceIndexDbFind;

		/// <summary> «Carrousel: YES»
		/// Stavové proměnné modulu ProductData, které jsou závislé na verzi plánu (se změnou plánu se aktivují jeho stavové proměnné)
		/// </summary>
		private ProductDataStatusCls _Status;

		#endregion
		#region PROPERTY
		/// <summary>
		/// Celkový počet vět
		/// </summary>
		public int TotalRecordCount
		{
			get
			{
				return this._ProductOrderDict.Count +
					   this._ProductOrderOperationDict.Count +
					   this._ProductOrderStructureDict.Count +
					   this._ProductOrderResourceAllocDict.Count;
			}
		}
		#endregion
		#region NAČTENÍ DAT Z DATABÁZE
		/// <summary>
		/// Metoda určená pro načtení dat z databáze. Volá se explicitně.
		/// V případě požadavku (true) vrátí velikost obsazené paměti
		/// </summary>
		internal override long LoadData(bool measureMemoryUsage)
		{
			long memBefore = 0;
			long memFilled = 0;
			long memUsed = 0;
			if (measureMemoryUsage)
			{
				GC.Collect();
				memBefore = GC.GetTotalMemory(false);
			}

			this._LoadSimple();
			//this._LoadAOMods();

			if (measureMemoryUsage)
			{
				GC.Collect();
				memFilled = GC.GetTotalMemory(false);
				memUsed = memFilled - memBefore;
			}
			return memUsed;
		}
		/// <summary>
		/// Metoda určená pro vytvoření interních idnexů podle existujících dat.
		/// Je třeba volat base.CreateIndexes(); kde dojde k nastavení this.NeedReIndexing = false;.
		/// </summary>
		internal override void CreateIndexes()
		{
			this._CreateIndexes();
			base.CreateIndexes();
		}
		/// <summary>
		/// Načte do sebe jednoduchá data (dictionary)
		/// </summary>
		private void _LoadSimple()
		{
			this.DataPrepare(true);
		}
        /// <summary>
        /// Vytvoří potřebné indexy
        /// </summary>
        private void _CreateIndexes()
        {
            this.DataIndexing(true);
        }
        #endregion
        #region GENERÁTORY SQL SELECTŮ PRO NAČÍTÁNÍ DAT PRODUCT ORDERS
        /// <summary>
		/// SQL SELECT pro načtení hlaviček výrobních příkazů
		/// </summary>
		private string _SqlSelectProductOrder { get { return ProductOrderCls.SqlSelectExt + " WHERE " + ProductOrderCls.SqlRecordExt + " " + _SqlProductOrderDataSet; } }
		/// <summary>
		/// SQL SELECT pro načtení operací výrobních příkazů.
        /// Operace se musí načítat i tehdy, když jsou ukončené - protože:
        /// a) podle neexistence operací se usoudí, že VP nemá dokumentaci a pak se mu vytvoří implicitní z STPV a ta se zaplánuje
        /// b) i na kompletně odvedené operaci může viset několik komponent, které nejsou vydané - a přitom je zapotřebí je zaplánovat.
		/// </summary>
        /// <remarks>
        ///   status: Atribut reprezentuje stav operace VP:
        ///  N – nebylo doposud odvedeno požadované množství
        ///  A – operace byla označena jako odvedená (při ukončení VP). Není možné na tuto operaci vystavovat odváděcí doklad, ani není doklad zahrnut do algoritmu MRP.
        ///  J – automatické ukončení operace
        ///  S – doklad byl stornován. Při používání evidence odchylek výrobní dokumentace bude takto označena nepoužitá operace VP. Vznikne stornováním, nebo editací operace. Není možné na tuto operaci vystavovat odváděcí doklad, ani není doklad zahrnut do algoritmu MRP.
        /// </remarks>
        private string _SqlSelectProductOperationOrder { get { return ProductOrderOperationCls.SqlSelect + " WHERE lcs.product_order_operation.status <> 'S' and lcs.product_order_operation.product_order " + _SqlProductOrderDataSet; } }
		/// <summary>
		/// SQL SELECT pro načtení komponent výrobních příkazů
		/// </summary>
        private string _SqlSelectProductStructureOrder
        {
            get 
            { 
                string sql = ProductOrderStructureCls.SqlSelect + 
                    " WHERE lcs.product_order_structure.status in ('N') " + 
                    " and (sign(lcs.product_order_structure.qty - lcs.product_order_structure.qty_real) = sign(lcs.product_order_structure.qty))" +
                    " and lcs.product_order_structure.stock in (select skl.cislo_subjektu from lcs.sk_sklad skl where skl.planovat = 'A')" +
                    " and lcs.product_order_structure.product_order " + _SqlProductOrderDataSet;
                return sql;
            }
        }
		/// <summary>
		/// SQL SELECT pro načtení zdrojů výrobních příkazů
		/// </summary>
		private string _SqlSelectProductResourceAllocOrder { get { return ProductOrderResourceAllocCls.SqlSelect + " WHERE lcs.product_order_resource_alloc.product_order_operation in (select cislo_nonsubjektu from lcs.product_order_operation poo where poo.product_order " + _SqlProductOrderDataSet + ")"; } }
		/// <summary>
		/// SQL FILTER pro výběr množiny čísel subjektů výrobních příkazů, které budou načteny do zpracování v plánovacím procesu.
		/// Vrací text typu " in (select cislo_subjektu from ...)"
		/// </summary>
		private string _SqlProductOrderDataSet
		{
			get
			{
				string sql = @" in (select cislo_subjektu from {{TempTable}})";
				sql = sql.Replace("{{TempTable}}", PlanningDataCls.TMPTABLE_LOAD_PRODUCT_ORDER);
				return sql;
			}
		}
		/// <summary>
		/// Metoda je volaná na základě požadavku DataPrepare.
		/// Je volána tehdy, když se mají načíst číselníky a data, která jsou podkladem jak pro plánování, 
		/// tak pro načítání uložených dat plánu.
		/// </summary>
		/// <param name="withFixed">Požadavek na načítání i fixních dat, tj. takových, která nejsou závislá na verzi plánu.</param>
		internal override void DataPrepare(bool withFixed)
		{
            // Načíst data pokud je požadavek IsFixed, anebo pokud některá data neexistují:
			bool load = (withFixed ||
				this._StandardMfrOperationsDict == null ||
				this._ProductOrderDict == null ||
				this._ProductOrderOperationDict == null ||
				this._ProductOrderStructureDict == null ||
				this._ProductOrderResourceAllocDict == null);

			if (load)
			{
                using (var scope = Steward.TraceScopeBegin(Constants.TraceTypeProduct, "Load.ProductData", Constants.TraceKeyWordAplScheduler))
                {
                    this._ProductOrderDict = Db_Layer.GetDictionary<ProductOrderCls>(_SqlSelectProductOrder);
                    this._ProductOrderOperationDict = Db_Layer.GetDictionary<ProductOrderOperationCls>(_SqlSelectProductOperationOrder);
                    this._ProductOrderStructureDict = Db_Layer.GetDictionary<ProductOrderStructureCls>(_SqlSelectProductStructureOrder);
                    this._ProductOrderResourceAllocDict = Db_Layer.GetDictionary<ProductOrderResourceAllocCls>(_SqlSelectProductResourceAllocOrder);
                    this._StandardMfrOperationsDict = Db_Layer.GetDictionary<StandardMfrOperationsCls>(StandardMfrOperationsCls.SqlSelect);

                    scope.User = new string[]
                    {
                        "ProdOrd.Count = " + this._ProductOrderDict.Count.ToString(),
                        "Operation.Count = " + this._ProductOrderOperationDict.Count.ToString(),
                        "Structure.Count = " + this._ProductOrderStructureDict.Count.ToString(),
                        "Resource.Count = " + this._ProductOrderResourceAllocDict.Count.ToString(),
                        "StdOper.Count = " + this._StandardMfrOperationsDict.Count.ToString()
                    };
                }

				this._PoOperationIndexFind = null;
				this._PoStructureIndexFind = null;
				this._PoResourceIndexFind = null;

				this._ProductOrderDbDict = new Dictionary<int, ProductOrderCls>();
				this._ProductOrderOperationDbDict = new Dictionary<int, ProductOrderOperationCls>();
				this._ProductOrderStructureDbDict = new Dictionary<int, ProductOrderStructureCls>();
				this._ProductOrderResourceAllocDbDict = new Dictionary<int, ProductOrderResourceAllocCls>();

				this._PoOperationIndexDbFind = new IndexDictCls<ProductOrderOperationFindProductOrderCls, ProductOrderOperationCls>(IndexType.MultiRows);
				this._PoStructureIndexDbFind = new IndexDictCls<ProductOrderStructureFindOperationPoCls, ProductOrderStructureCls>(IndexType.MultiRows);
				this._PoResourceIndexDbFind = new IndexDictCls<ProductOrderResourceAllocFindProductOrderOperationCls, ProductOrderResourceAllocCls>(IndexType.MultiRows);
			
				this.NeedReIndexing = false;

				this.DataIndexing(withFixed);
			}

			// Pokud není připraven Status, připravím jej defaultní:
			if (this._Status == null)
				this._Status = new ProductDataStatusCls();


			// Uložím si aktuální stav dat (reference na datové objekty) do aktuální položky karuselu, což má být položka odpovídající aktuální verzi plánu:
			this.CarrouselStoreDataToCurrent();
		}
		/// <summary>
		/// Metoda je volaná na základě požadavku DataPrepare.
		/// Metodu si volá metoda DataPrepare().
		/// Je volána tehdy, když se načetly data do tabulek, a mají se vytvořit paměťové indexy.
		/// </summary>
		/// <param name="withFixed">Požadavek na indexování i fixních dat, tj. takových, která nejsou závislá na verzi plánu.</param>
		internal override void DataIndexing(bool withFixed)
		{
			if (this._PoOperationIndexFind == null)
				this._PoOperationIndexFind = new IndexDictCls<ProductOrderOperationFindProductOrderCls, ProductOrderOperationCls>(this._ProductOrderOperationDict, IndexType.MultiRows);
			if (this._PoStructureIndexFind == null)
				this._PoStructureIndexFind = new IndexDictCls<ProductOrderStructureFindOperationPoCls, ProductOrderStructureCls>(this._ProductOrderStructureDict, IndexType.MultiRows);
			if (this._PoResourceIndexFind == null)
				this._PoResourceIndexFind = new IndexDictCls<ProductOrderResourceAllocFindProductOrderOperationCls, ProductOrderResourceAllocCls>(this._ProductOrderResourceAllocDict, IndexType.MultiRows);

			if (this._PoOperationIndexDbFind == null)
				this._PoOperationIndexDbFind = new IndexDictCls<ProductOrderOperationFindProductOrderCls, ProductOrderOperationCls>(this._ProductOrderOperationDbDict, IndexType.MultiRows);
			if (this._PoStructureIndexDbFind == null)
				this._PoStructureIndexDbFind = new IndexDictCls<ProductOrderStructureFindOperationPoCls, ProductOrderStructureCls>(this._ProductOrderStructureDbDict, IndexType.MultiRows);
			if (this._PoResourceIndexDbFind == null)
				this._PoResourceIndexDbFind = new IndexDictCls<ProductOrderResourceAllocFindProductOrderOperationCls, ProductOrderResourceAllocCls>(this._ProductOrderResourceAllocDbDict, IndexType.MultiRows);

			this.NeedReIndexing = false;
		}
        #endregion
        #region VERZE PLÁNU - PŘEPÍNÁNÍ. NAČÍTÁNÍ DAT Z DATABÁZE, INDEXOVÁNÍ
        /// <summary>
        /// Aktivace verze plánu
        /// </summary>
        /// <param name="planCVersion"></param>
        internal override void ActivateDataVersion(int planCVersion)
        {
            // Jak to funguje?   Popisek je v PlanningDataCls.ActivateDataVersion(), odkud se volá zdejší metoda.

            // Uložit současná data do současného karuselu:
            if (this.CurrentPlanCVersion != 0)
            {
                // Přetočit karusel na nová data (najít nebo vytvořit nový segment odpovídající číslu plánu):
                this.CarrouselSelectItem(this.CurrentPlanCVersion);

                // Uložit si do karuselu moje data, která jsou závislá na verzi plánu:
                this.CarrouselStoreDataToCurrent();
            }

            // Přetočit karusel na nová data (najít nebo vytvořit nový segment odpovídající číslu plánu):
            this.CarrouselSelectItem(planCVersion);

            // Vyzvednout z karuselu data nové verze (případně null, ale pak se bude volat DataPrepare):
            this._Status = this.CarrouselCurrentItem.Get<ProductDataStatusCls>((int)CarrouselDataType.Status);

            // Zapamatovat si, že máme aktivní verzi plánu:
            this.CurrentPlanCVersion = planCVersion;
        }
        /// <summary>
        /// Uloží nynější datové objekty do aktuální položky karuselu (CarrouselCurrentItem).
        /// Virtuální metoda, určená k přepsání.
        /// Měla by se volat v průběhu aktivace verze dat z metody ActivateDataVersion() v její první části 
        ///  (kdy ukládám nynější data do položky karuselu this.CurrentPlanCVersion),
        /// a pak by se měla volat z metody DataPrepare(), na jejím úplném konci, když jsou všechna data korektně připravena.
        /// Obsahem metody má být opakované volání this.CarrouselCurrentItem.Set(code, data) pro všechna data, která jsou závislá na verzi.
        /// </summary>
        protected override void CarrouselStoreDataToCurrent()
        {
            // Výrobní příkazy zatím nejsou závislé na verzi plánu, nebudeme nic ukládat do karuselu. Jen Status:
            this.CarrouselCurrentItem.Set((int)CarrouselDataType.Status, this._Status);
        }
        /// <summary>
		/// Enum, který pojmenovává jednotlivé datové prvky, které tento modul ukládá do / načítá z karuselu.
		/// Enum slouží jako klíč k položkám karuselu.
		/// </summary>
		internal enum CarrouselDataType : int
		{
			Status = 1,
			WhDocItem,
			StockCard,
			StockCardIdxSU,
			StockCardIdxSL,
			StockCardIdxU,
			StockCardGridSL
		}
		#endregion
		#region VYHLEDÁNÍ DAT ZÁKLADNÍ - DLE KLÍČE VĚTY
		/// <summary>
		/// Najde a vrátí data hledaného výrobního příkazu.
		/// Pokud jej nenajde, vrací null.
		/// </summary>
		/// <param name="key">Číslo subjektu výrobního příkazu</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		/// <returns></returns>
		internal ProductOrderCls FindProductOrder(int key, NotFindMode notFindMode)
		{
			return this._FindItem<ProductOrderCls>(key, notFindMode, this._ProductOrderDict, this._ProductOrderDbDict, this._LoadProductOrderFromDb);
		}
		/// <summary>
		/// Najde a vrátí data hledané operace výrobního příkazu.
		/// Pokud ji nenajde, vrací null.
		/// </summary>
		/// <param name="key">Číslo nonsubjektu operace výrobního příkazu</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		/// <returns></returns>
		internal ProductOrderOperationCls FindProductOrderOperation(int key, NotFindMode notFindMode)
		{
			return this._FindItem<ProductOrderOperationCls>(key, notFindMode, this._ProductOrderOperationDict, this._ProductOrderOperationDbDict, this._LoadProductOrderOperationFromDb);
		}
		/// <summary>
		/// Najde a vrátí data hledané komponenty výrobního příkazu.
		/// Pokud ji nenajde, vrací null.
		/// </summary>
		/// <param name="key">Číslo nonsubjektu komponenty výrobního příkazu</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		/// <returns></returns>
		internal ProductOrderStructureCls FindProductOrderStructure(int key, NotFindMode notFindMode)
		{
			return this._FindItem<ProductOrderStructureCls>(key, notFindMode, this._ProductOrderStructureDict, this._ProductOrderStructureDbDict, this._LoadProductOrderStructureFromDb);
		}
		/// <summary>
		/// Najde a vrátí data hledaného zdroje operace výrobního příkazu.
		/// Pokud jej nenajde, vrací null.
		/// </summary>
		/// <param name="key">Číslo nonsubjektu zdroje operace výrobního příkazu</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		/// <returns></returns>
		internal ProductOrderResourceAllocCls FindProductOrderResource(int key, NotFindMode notFindMode)
		{
			return this._FindItem<ProductOrderResourceAllocCls>(key, notFindMode, this._ProductOrderResourceAllocDict, this._ProductOrderResourceAllocDbDict, this._LoadProductOrderResourceFromDb);
		}
		/// <summary>
		/// Najde a vrátí standardní operaci podle jejího klíče
		/// </summary>
		/// <param name="key">Číslo subjektu standardní operace</param>
		/// <returns></returns>
		internal StandardMfrOperationsCls FindStandardMfrOperations(int key)
		{
			this.CheckIndexes();
			StandardMfrOperationsCls standardMfrOperations;
			if (this._StandardMfrOperationsDict.TryGetValue(key, out standardMfrOperations))
				return standardMfrOperations;
			return null;
		}
		#endregion
		#region VYHLEDÁNÍ DAT ROZŠÍŘENÉ
		/// <summary>
		/// Metoda vrátí seznam všech načtených výrobních příkazů, bez jakéhokoli filtrování.
		/// Vrací ty výrobní příkazy, které se mají zaplánovat (tj. mají na sobě něco, co má vliv na plán).
		/// Neobsahuje výrobní příkazy, které jsou ukončené, a dostaly se do paměti na základě explicitního požadavku na donačtení.
		/// (To nastane tehdy, když existuje plán, pak je některý VP ukončen, a pak se plán znovu načítá = některé jeho položky
		/// se odkazují na výrobní příkazy, které do nového plánu nepatří)
		/// </summary>
		/// <returns></returns>
		internal List<ProductOrderCls> GetProductOrderList()
		{
			return new List<ProductOrderCls>(this._ProductOrderDict.Values);
		}
        /// <summary>
        /// Metoda určí, zda daný výrobní příkaz má nějaké operace.
        /// Vrací true, pokud existuje jakákoli operace, i když by byla celá vyřízená (odvedená, stornovaná, atd).
        /// </summary>
        /// <param name="productOrder"></param>
        /// <returns></returns>
        internal bool ProductOrderHasAnyOperation(int productOrder)
        {
            ProductOrderCls poData = this.FindProductOrder(productOrder, NotFindMode.GetFromDbNull);
			if (productOrder == null)
				return false;
            return poData.ProductOrderOperationExists;
        }
		/// <summary>
		/// Metoda najde a vrátí seznam všech operací ze všech výrobních příkazů.
		/// </summary>
		/// <returns></returns>
        internal IEnumerable<ProductOrderOperationCls> GetProductOrderOperations()
        {
            return this._ProductOrderOperationDict.Values;
        }
		/// <summary>
		/// Metoda najde a vrátí seznam všech fixovaných operací ze všech výrobních příkazů.
		/// Tento seznam je použitý na začátku plánování, kdy se prioritně plánují fixované operace do neomezených kapacit, 
		/// a teprve až poté probíhá standardní plánovací proces.
		/// </summary>
		/// <returns></returns>
		internal List<ProductOrderOperationCls> GetProductOrderFixedOperation()
		{
			List<ProductOrderOperationCls> result = new List<ProductOrderOperationCls>();
			foreach (ProductOrderOperationCls operation in this._ProductOrderOperationDict.Values)
			{
                int fixedOperation = (operation.FixedOperation.IsNull ? 0 : operation.FixedOperation.Value);
                if (fixedOperation == 1 || fixedOperation == 2)
					result.Add(operation);
			}
			return result;
		}
        /// <summary>
        /// Najde a vrátí operaci VP podle jejího nonsubjektu.
        /// Nenajde-li, vrátí null.
        /// </summary>
        /// <param name="productOrderOperation"></param>
        /// <returns></returns>
        internal ProductOrderOperationCls FindOperation(int productOrderOperation)
        {
            ProductOrderOperationCls data;
            if (this._ProductOrderOperationDict.TryGetValue(productOrderOperation, out data))
                return data;
            return null;
        }
		/// <summary>
		/// Najde a vrátí soupis operací pro daný Výrobní příkaz.
		/// Pro neexistující Výrobní příkaz vrátí null.
		/// Pokud Výrobní příkaz existuje, ale nemá operace, vrátí prázdný seznam (!= null, Count = 0).
		/// Volitelně setřídí ascending/descending.
		/// Třídění se provádí podle hodnot Line, Appendix, CisloNonsubjektu.
		/// Vygenerovaný seznam nikdy neobsahuje prvky s hodnotou null.
		/// </summary>
		/// <param name="productOrder">Klíč výrobního příkazu</param>
		/// <param name="sortOrder">Požadavek na pořadí třídění: ToFuture = ASCENDING, ToHistory = DESCENDING, Stay = netřídit</param>
		/// <returns></returns>
		public List<ProductOrderOperationCls> FindOperationListForProductOrder(int productOrder, TimeRange.TimeDirection sortOrder)
		{
			this.CheckIndexes();

			// Pro neexistující výrobní příkaz vrátí null.
            ProductOrderCls poData = this.FindProductOrder(productOrder, NotFindMode.GetFromDbNull);
			if (productOrder == null)
				return null;

			// Najdeme všechny klíče operací pro daný VP:
			List<int> operList = this._PoOperationIndexFind.SeekAll(productOrder);

			// Dohledáme vlastní data operací:
			List<ProductOrderOperationCls> result = new List<ProductOrderOperationCls>();
			if (operList != null)
			{
				foreach (int operation in operList)
				{
					ProductOrderOperationCls operData;
					if (this._ProductOrderOperationDict.TryGetValue(operation, out operData))
					{
						if (operData != null)
							result.Add(operData);
					}
				}

				// Setřídění:
				if (result.Count > 1)
				{
					switch (sortOrder)
					{
						case TimeRange.TimeDirection.ToFuture:
							result.Sort(_CompareProductOrderOperationAsc);
							break;
						case TimeRange.TimeDirection.ToHistory:
							result.Sort(_CompareProductOrderOperationDesc);
							break;
					}
				}
			}
			return result;
		}
		/// <summary>
		/// Vygeneruje a naplní seznam komponent, které vstupují do určité operace Výrobního příkazu.
		/// Vrátí List objektů třídy ProductOrderStructureCls.
		/// Pokud neexistuje daná operace, vrací null.
		/// Pokud operace existuje, ale nemá komponenty, vrací prázdný seznam.
		/// Vygenerovaný seznam nikdy neobsahuje prvky s hodnotou null.
		/// </summary>
		/// <param name="productOrderOperation">Klíč operace Výrobního příkazu</param>
		/// <returns></returns>
		internal List<ProductOrderStructureCls> FindProductOrderStructureListForPoOper(int productOrderOperation)
		{
			// Pro neexistující operaci výrobního příkazu vrátí null.
			if (productOrderOperation == 0) return null;

			// Najdeme všechny klíče komponent dané operace výrobního příkazu:
			List<int> strList = this._PoStructureIndexFind.SeekAll(productOrderOperation);

			// Dohledáme vlastní data komponent:
			List<ProductOrderStructureCls> result = new List<ProductOrderStructureCls>();
			if (strList != null)
			{
				foreach (int structure in strList)
				{
					ProductOrderStructureCls structData;
					if (this._ProductOrderStructureDict.TryGetValue(structure, out structData))
					{
						if (structData != null)
							result.Add(structData);
					}
				}
			}

			return result;
		}
		/// <summary>
		/// Vygeneruje a naplní seznam kapacitních zdrojů, které vstupují do určité operace Výrobního příkazu.
		/// Vrátí List objektů třídy ProductOrderResourceAllocCls.
		/// Pokud neexistuje daná operace, vrací null.
		/// Pokud operace existuje, ale nemá zdroje, vrací prázdný seznam.
		/// Vygenerovaný seznam nikdy neobsahuje prvky s hodnotou null.
		/// </summary>
		/// <param name="productOrderOperation">Klíč operace Výrobního příkazu</param>
		/// <returns></returns>
		internal List<ProductOrderResourceAllocCls> FindProductOrderResourceListForPoOper(int productOrderOperation)
		{
			// Pro neexistující operaci výrobního příkazu vrátí null.
			if (productOrderOperation == 0) return null;

			// Najdeme všechny klíče zdrojů dané operace výrobního příkazu:
			List<int> resList = this._PoResourceIndexFind.SeekAll(productOrderOperation);

			// Dohledáme vlastní data komponent:
			List<ProductOrderResourceAllocCls> result = new List<ProductOrderResourceAllocCls>();
			if (resList != null)
			{
				foreach (int resource in resList)
				{
					ProductOrderResourceAllocCls resData;
					if (this._ProductOrderResourceAllocDict.TryGetValue(resource, out resData))
					{
						if (resData != null)
							result.Add(resData);
					}
				}
			}

			return result;
		}
		#endregion
		#region VYHLEDÁNÍ DAT, KTERÁ SE NENAČETLA V ZÁKLADNÍM SEZNAMU VYRÁBĚNÝCH POLOŽEK
		/// <summary>
		/// Vyhledá a vrátí záznam daného (generického) typu, a to podle této logiky:
		/// Nejprve hledá v tabulce dat v paměti. Pokud najde, vrátí jej.
		/// Pak vyhodnotí parametr (notFindMode), a pokud nemá hledat v databázi, tak vrátí Empty nebo null (jak je požadováno).
		/// Pak se podívá do tabulky dat donačtených z databáze. Pokud tam najde záznam, vrátí ho.
		/// Pokud tam najde informaci, že už byl požadován ale nebyl nalezen, tak vrátí Empty nebo null (jak je požadováno).
		/// Pokud jsme takový záznam z DB ještě nenačítali, volá danou metodu pro načtení.
		/// Vrací výsledek, nebo Empty nebo null.
		/// </summary>
		/// <typeparam name="T">Typ datového záznamu, který se vrací, který je uložen v předaných Dictionary, a se kterým pracuje metoda (delegát) dbLoadMethod</typeparam>
		/// <param name="key">Klíč hledaného záznamu</param>
		/// <param name="notFindMode">Režim chování v případě nenalezení</param>
		/// <param name="memData">Data načítaná z paměti (základní balík)</param>
		/// <param name="dbData">Data načítaná z databáze (explicitní požadavky)</param>
		/// <param name="dbLoadMethod">Odkaz na metodu, která načítá data z databáze</param>
		/// <returns></returns>
		private T _FindItem<T>(int key, NotFindMode notFindMode, Dictionary<int, T> memData, Dictionary<int, T> dbData, Func<int, T> dbLoadMethod) where T : class, new()
		{
			this.CheckIndexes();

			T result;
			if (memData.TryGetValue(key, out result))
				return result;

			// Nenašli jsme data v paměti. Co máme dělat?
			bool fromDb = (notFindMode == NotFindMode.GetFromDbEmpty || notFindMode == NotFindMode.GetFromDbNull);  // Mám číst z databáze
			bool empty = (notFindMode == NotFindMode.Empty || notFindMode == NotFindMode.GetFromDbEmpty);           // Mám vracet Empty

			// Pokud se nemá vracet nic z databáze, vrátím Empty nebo null:
			if (!fromDb)
				return (T)(empty ? new T() : null);

			// Podívám se do seznamu dat, která jsme již dříve donačetli z databáze:
			if (dbData.TryGetValue(key, out result))
			{	// V paměti už něco máme?
				if (result != null) return result;            // A jsou v paměti data?  Vrátíme je.

				// Vrátím Empty nebo null:
				return (T)(empty ? new T() : null);
			}

			// V seznamu to zatím není, zavolám načítací metodu:
			result = dbLoadMethod(key);
			if (result != null) return result;            // Pokud se načetlo něco, vrátíme to něco.
			return (T)(empty ? new T() : null);           // Jinak vrátím Empty nebo null.
		}
		/// <summary>
		/// Metoda načte z databáze záznam daného výrobního příkazu a vrátí jej.
		/// Současně načte celou dokumentaci tohoto výrobního příkazu.
		/// Metoda je volána z metody this._FindItem() jako delegát "dbLoadMethod".
		/// Volbu konkrétní metody provádí nadřízená metoda this.FindProductOrder()
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		private ProductOrderCls _LoadProductOrderFromDb(int key)
		{
			this._LoadProductDataFromDb(key);
			return _FindLoadedItem<ProductOrderCls>(key, this._ProductOrderDbDict);
		}
		/// <summary>
		/// Metoda načte z databáze záznam dané operace výrobního příkazu a vrátí ji.
		/// Současně načte celou dokumentaci tohoto výrobního příkazu.
		/// Metoda je volána z metody this._FindItem() jako delegát "dbLoadMethod".
		/// Volbu konkrétní metody provádí nadřízená metoda this.FindProductOrderOperation()
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		private ProductOrderOperationCls _LoadProductOrderOperationFromDb(int key)
		{
			int productOrder = Db_Layer.GetItem<int>("SELECT product_order FROM lcs.product_order_operation WHERE cislo_nonsubjektu = " + SqlFormat.ToSql(key));
			this._LoadProductDataFromDb(productOrder);
			return _FindLoadedItem<ProductOrderOperationCls>(key, this._ProductOrderOperationDbDict);
		}
		/// <summary>
		/// Metoda načte z databáze záznam dané komponenty výrobního příkazu a vrátí ji.
		/// Současně načte celou dokumentaci tohoto výrobního příkazu.
		/// Metoda je volána z metody this._FindItem() jako delegát "dbLoadMethod".
		/// Volbu konkrétní metody provádí nadřízená metoda this.FindProductOrderStructure()
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		private ProductOrderStructureCls _LoadProductOrderStructureFromDb(int key)
		{
			int productOrder = Db_Layer.GetItem<int>("SELECT product_order FROM lcs.product_order_structure WHERE cislo_nonsubjektu = " + SqlFormat.ToSql(key));
			this._LoadProductDataFromDb(productOrder);
			return _FindLoadedItem<ProductOrderStructureCls>(key, this._ProductOrderStructureDbDict);
		}
		/// <summary>
		/// Metoda načte z databáze záznam daného zdroje operace výrobního příkazu a vrátí jej.
		/// Současně načte celou dokumentaci tohoto výrobního příkazu.
		/// Metoda je volána z metody this._FindItem() jako delegát "dbLoadMethod".
		/// Volbu konkrétní metody provádí nadřízená metoda this.FindProductOrderResource()
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		private ProductOrderResourceAllocCls _LoadProductOrderResourceFromDb(int key)
		{
			int productOrder = Db_Layer.GetItem<int>("SELECT poo.product_order FROM lcs.product_order_resource_alloc por join lcs.product_order_operation poo on poo.cislo_nonsubjektu = por.product_order_operation WHERE por.cislo_nonsubjektu = " + SqlFormat.ToSql(key));
			this._LoadProductDataFromDb(productOrder);
			return _FindLoadedItem<ProductOrderResourceAllocCls>(key, this._ProductOrderResourceAllocDbDict);
		}
		/// <summary>
		/// Metoda najde a vrátí záznam z daného klíče dané dictionary. Neřeší, co tam najde (zda data nebo null).
		/// Pokud nenajde hledaný klíč, přidá jej a vloží do něj null.
		/// </summary>
		/// <typeparam name="T">Typ datového záznamu, který se vrací, a který je uložen v předané Dictionary</typeparam>
		/// <param name="key">Klíč hledaného záznamu</param>
		/// <param name="dbData">Data načítaná z databáze (explicitní požadavky)</param>
		/// <returns></returns>
		private T _FindLoadedItem<T>(int key, Dictionary<int, T> dbData) where T : class, new()
		{
			T result;
			if (dbData.TryGetValue(key, out result))
				return result;

			dbData.Add(key, result);
			return result;
		}
		/// <summary>
		/// Načte z databáze do tabulek (typu *DbDict) všechny záznamy k danému výrobnímu příkazu.
		/// Uživatel si vyžádal jeden konkrétní záznam tohoto VP, ale systém načte všechny záznamy tohoto VP,
		/// protože má za úkol držet v paměti kompletní sadu dat za každý VP.
		/// Načítá tedy všechny typy záznamů: hlavička, operace, komponenty, zdroje operací.
		/// </summary>
		/// <param name="productOrder"></param>
		private void _LoadProductDataFromDb(int productOrder)
		{
			// Výrobní příkaz:
			if (productOrder <= 0) return;                   // Pokud je na vstupu nesmysl, nebudu nic načítat.
			ProductOrderCls productOrderData = Db_Layer.GetRecord<ProductOrderCls>(ProductOrderCls.SqlSelectExt + " WHERE " + ProductOrderCls.SqlRecordExt + " = " + SqlFormat.ToSql(productOrder));
			if (productOrderData == null) return;            // Pokud nenačtu hlavičku VP, nebudu ani načítat další soupisy.
			this._LoadProductDataAddRecord<ProductOrderCls>(productOrderData, this._ProductOrderDbDict);

			// Jeho operace:
			List<ProductOrderOperationCls> pooList = Db_Layer.GetList<ProductOrderOperationCls>(ProductOrderOperationCls.SqlSelect + " WHERE lcs.product_order_operation.product_order = " + SqlFormat.ToSql(productOrder));
			this._LoadProductDataAddList<ProductOrderOperationCls>(pooList, this._ProductOrderOperationDbDict, this._PoOperationIndexDbFind);

			// Jeho komponenty:
			List<ProductOrderStructureCls> posList = Db_Layer.GetList<ProductOrderStructureCls>(ProductOrderStructureCls.SqlSelect + " WHERE lcs.product_order_structure.product_order = " + SqlFormat.ToSql(productOrder));
			this._LoadProductDataAddList<ProductOrderStructureCls>(posList, this._ProductOrderStructureDbDict, this._PoStructureIndexDbFind);

			// Jeho zdroje operací:
			List<ProductOrderResourceAllocCls> porList = Db_Layer.GetList<ProductOrderResourceAllocCls>(ProductOrderResourceAllocCls.SqlSelect + " join lcs.product_order_operation poo on poo.cislo_nonsubjektu = lcs.product_order_resource_alloc.product_order_operation WHERE poo.product_order = " + SqlFormat.ToSql(productOrder));
			this._LoadProductDataAddList<ProductOrderResourceAllocCls>(porList, this._ProductOrderResourceAllocDbDict, this._PoResourceIndexDbFind);
		}
		/// <summary>
		/// Metoda pro přidání všech položek vstupního seznamu do dané Dictionary plus Indexu.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="itemList"></param>
		/// <param name="dbData"></param>
		/// <param name="index"></param>
		private void _LoadProductDataAddList<T>(List<T> itemList, Dictionary<int, T> dbData, IIndexDict<T> index) where T : IDbRecord
		{
			foreach (T item in itemList)
			{
				this._LoadProductDataAddRecord(item, dbData);
				index.Add(item.RecordNumber, item);
			}
		}
		/// <summary>
		/// Daný záznam vloží (Add/Replace) do daného seznamu Dictionary.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="item"></param>
		/// <param name="dbData"></param>
		private void _LoadProductDataAddRecord<T>(T item, Dictionary<int, T> dbData) where T : IDbRecord
		{
			if (dbData.ContainsKey(item.RecordNumber))
				dbData[item.RecordNumber] = item;
			else
				dbData.Add(item.RecordNumber, item);
		}
		#endregion
		#region PODPORA PRO TVORBU ZÁZNAMU MATERIÁLOVÉ OSY
		/// <summary>
		/// Vygeneruje záznam materiálové osy pro příjem z daného výrobního příkazu.
		/// Vygeneruje záznam i pro příjem množství = 0, 
		/// protože i pro takový záznam je nutné zaregistrovat výrobní kapacity a zadat na osu požadavky na výdej komponent.
		/// </summary>
		/// <param name="productOrder">Výrobní příkaz</param>
		/// <param name="fillWhData">Handler, který pro připravovaný záznam osy S doplní další skladová data, která na záznamu VP nejsou (z kmenové karty, ze stavu skladu, ...)</param>
		/// <returns></returns>
		internal MaterialPlanAxisItemCls CreateMaterialAxisFromPO(ProductOrderCls productOrder, ProcessAxis fillWhData)
		{
			// Kontroly vstupního záznamu:
			if (productOrder.Stock <= 0)
			{
				this.Auditor.AddInfo(PlanningResultSeverityType.UserError, PlanningResultCircumstanceType.WarehouseDocumentNotConsistent, (IRecordItemKey)productOrder,
					MessageInfo.Get("Na výrobním příkazu %0 není vyplněn sklad.", productOrder.ReferNazev));
				return null;
			}

			// Informace o výrobním příkazu, podle nichž založím záznam osy:
			DateTime datePlanEnd = (!productOrder.DateEndPlan.IsNull ? productOrder.DateEndPlan.Value : productOrder.DateStartPlan);
			PlanSAxisChangeType change = PlanSAxisChangeType.IncrementByRealProductOrder;
			decimal qty = productOrder.QtyAxis;           // Množství dosud nepřijaté, anebo 0 (nikdy ne záporné)
            bool isFixedAxis = PlanningComponentCls.MfrFixedToIsFixed(productOrder.MfrFixedProductOrder);
            bool hasOperation = this.ProductOrderHasAnyOperation(productOrder.RecordNumber);

            // Operace VP, je některá fixovaná? Pokud ano, pak je fixovaný záznam osy S (a nebude se měnit jeho datum DatePlanEnd):
            List<ProductOrderOperationCls> operations = this.FindOperationListForProductOrder(productOrder.RecordNumber, TimeRange.TimeDirection.ToFuture);
            if (operations.Any(poo => poo.IsFixedOperation))
                isFixedAxis = true;

			// Záznam osy, do něj budeme vkládat další data:
            MaterialPlanAxisItemCls axis = MaterialPlanAxisItemCls.CreateFrom(datePlanEnd, change, qty, isFixedAxis, productOrder.GId, 0);
			axis.Description = productOrder.Note;
            axis.QtyPropWithLoss = productOrder.QtyAxisWithLoss;
            axis.DateRealBegin = productOrder.DateStartPlan;
            if (operations.Count > 0)
                axis.DateRealBegin = operations[0].DateStartPlan;

			// Sklad, zdroj, skladová karta, dodavatel, kmenová karta, atd (pokud nenajdu zdroj nebo sklad, nenaplní se Source a skončím):
			axis.SetMaterial(productOrder.Source, productOrder.Variation, productOrder.OrderRelation, productOrder.Stock);
			fillWhData(ref axis);  // Na základě dat z předešlého řádku dohledá a vepíše na osu data z kmenové karty a ze stavu skladu, určí variantu, zakázku (i interní a plánovací), a určí plánovací jednotku

			// Doplníme klíč VP, a dílec a T modifikaci z hlavičky VP:
			axis.SetProductOrder(productOrder.RecordNumber);
			axis.SetTpvData(productOrder.ConstrElement, productOrder.AOperationMod);

			// Kontrola: pokud plnění skladovými daty nenašlo záznamy, je zde 0:
			if (axis.PlanUnitS == 0)
			{
				this.Auditor.AddInfo(PlanningResultSeverityType.UserError, PlanningResultCircumstanceType.ProductOrderNotConsistent, (IRecordKey)productOrder,
					MessageInfo.Get("Pro výrobní příkaz %0 nebyla určena plánovací jednotka (nebo sklad nebo kmenová karta).", productOrder.ReferNazev));
				return null;
			}

			// Atributy:
            axis.SetAttributes(PlanSAxisAttributes.ProductOrderWithNoOperation, (!hasOperation));

			return axis;
		}
		#endregion
		#region PRIVÁTNÍ METODY
		/// <summary>
		/// Komparátor operací ProductOrderOperationCls, ASCENDING
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		private static int _CompareProductOrderOperationAsc(ProductOrderOperationCls a, ProductOrderOperationCls b)
		{
			int cmp;
			cmp = a.Line.CompareTo(b.Line);
			if (cmp != 0) return cmp;
			cmp = a.LineApndx.CompareTo(b.LineApndx);
			if (cmp != 0) return cmp;
			return a.RecordNumber.CompareTo(b.RecordNumber);
		}
		/// <summary>
		/// Komparátor operací ProductOrderOperationCls, DESCENDING
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		private static int _CompareProductOrderOperationDesc(ProductOrderOperationCls a, ProductOrderOperationCls b)
		{
			return _CompareProductOrderOperationAsc(b, a);
		}
		#endregion
		#region SUBCLASSES PRO INDEXY
		#region INDEX ProductOrderOperationFindProductOrderCls : hledá data třídy ProductOrderOperationCls, podle hodnoty ProductOrder
		/// <summary>
		/// Třída pro tvorbu indexu tabulky se záznamy třídy ProductOrderOperationCls, index podle hodnoty ProductOrder.
		/// Umožní najít všechny záznamy typu: Data z Master tabulky třídy 1190: Operace výrobního příkazu (ProductOrderOperationInst), Nonsubjektová 
		/// pro danou hodnotu: Vztah 8938: Výrobní příkaz (zprava: Operace výrobního příkazu)
		/// </summary>
		protected class ProductOrderOperationFindProductOrderCls : IndexInt32Cls<ProductOrderOperationCls>
		{
			public override bool FillFrom(ProductOrderOperationCls record)
			{
				this.Value = record.ProductOrder;
				return true;
			}
			public static implicit operator ProductOrderOperationFindProductOrderCls(int value)
			{
				ProductOrderOperationFindProductOrderCls key = new ProductOrderOperationFindProductOrderCls();
				key.Value = value;
				return key;
			}
		}
		#endregion
		#region INDEX ProductOrderStructureFindOperationPoCls : hledá data třídy ProductOrderStructureCls, podle hodnoty OperationPo
		/// <summary>
		/// Třída pro tvorbu indexu tabulky se záznamy třídy ProductOrderStructureCls, index podle hodnoty OperationPo.
		/// Umožní najít všechny záznamy typu: Data z Master tabulky třídy 1189: Komponenta výrobního příkazu (ProductOrderStructureInst), Nonsubjektová 
		/// pro danou hodnotu: Vztah 8933: Operace (zprava: Komponenty výrobního příkazu)
		/// </summary>
		protected class ProductOrderStructureFindOperationPoCls : IndexInt32Cls<ProductOrderStructureCls>
		{
			public override bool FillFrom(ProductOrderStructureCls record)
			{
				this.Value = record.OperationPo;
				return true;
			}
			public static implicit operator ProductOrderStructureFindOperationPoCls(int value)
			{
				ProductOrderStructureFindOperationPoCls key = new ProductOrderStructureFindOperationPoCls();
				key.Value = value;
				return key;
			}
		}
		#endregion
		#region INDEX ProductOrderResourceAllocFindProductOrderOperationCls : hledá data třídy ProductOrderResourceAllocCls, podle hodnoty ProductOrderOperation
		/// <summary>
		/// Třída pro tvorbu indexu tabulky se záznamy třídy ProductOrderResourceAllocCls, index podle hodnoty ProductOrderOperation.
		/// Umožní najít všechny záznamy typu: Data z Master tabulky třídy 1441: Zdroj operace VP (ProductOrderResourceAllocInst), Nonsubjektová 
		/// pro danou hodnotu: Vztah 100836: Operace VP (zprava: Zdroje operace)
		/// </summary>
		protected class ProductOrderResourceAllocFindProductOrderOperationCls : IndexInt32Cls<ProductOrderResourceAllocCls>
		{
			public override bool FillFrom(ProductOrderResourceAllocCls record)
			{
				this.Value = record.ProductOrderOperation;
				return true;
			}
			public static implicit operator ProductOrderResourceAllocFindProductOrderOperationCls(int value)
			{
				ProductOrderResourceAllocFindProductOrderOperationCls key = new ProductOrderResourceAllocFindProductOrderOperationCls();
				key.Value = value;
				return key;
			}
		}
		#endregion

		#endregion
		#region PODPORA PRO TESTY - PLNĚNÍ EXPLICITNÍMI DATY
		/// <summary>
		/// Do systému ProductData vloží další výrobní příkaz
		/// </summary>
		/// <param name="record">Vkládaný záznam</param>
		internal void AddProductOrder(RecordCls record)
		{
			this._AddRecord(this._ProductOrderDict, record);
		}
		/// <summary>
		/// Do systému ProductData vloží další operaci výrobního příkazu
		/// </summary>
		/// <param name="record">Vkládaný záznam</param>
		internal void AddProductOrderOperation(RecordCls record)
		{
			this._AddRecord(this._ProductOrderOperationDict, record);
		}
		/// <summary>
		/// Do systému ProductData vloží další komponentu výrobního příkazu
		/// </summary>
		/// <param name="record">Vkládaný záznam</param>
		internal void AddProductOrderStructure(RecordCls record)
		{
			this._AddRecord(this._ProductOrderStructureDict, record);
		}
		/// <summary>
		/// Do systému ProductData vloží další zdroj výrobního příkazu
		/// </summary>
		/// <param name="record">Vkládaný záznam</param>
		internal void AddProductOrderResource(RecordCls record)
		{
			this._AddRecord(this._ProductOrderResourceAllocDict, record);
		}
		/// <summary>
		/// Do systému ProductData vloží další standardní operaci
		/// </summary>
		/// <param name="record">Vkládaný záznam</param>
		internal void AddStandardMfrOperations(RecordCls record)
		{
			this._AddRecord(this._StandardMfrOperationsDict, record);
		}
		/// <summary>
		/// Do soupisu vět přidá další větu. Pokud věta s tímto číslem již existuje, přepíše ji.
		/// </summary>
		/// <param name="dictionary">Soupis dat</param>
		/// <param name="record">Věta</param>
		private void _AddRecord(System.Collections.IDictionary dictionary, RecordCls record)
		{
			if (dictionary.Contains(record.RecordNumber))
				dictionary[record.RecordNumber] = record;
			else
				dictionary.Add(record.RecordNumber, record);

			this.NeedReIndexing = true;
		}
		#endregion
		#region IMPLEMENTACE INTERFACE ITechnologyLibrary
		/// <summary>
		/// Vrací typ dat, které jsou tímto zdrojem poskytované.
		/// Tato třída vrací konstantně TDocumentType.Production.
		/// </summary>
		TDocumentType ITechnologyLibrary.CurrentType { get { return TDocumentType.Production; } }
		/// <summary>
		/// true, pokud tento zdroj technologie podporuje fiktivní dílce (komponenta operace, která představuje vložený jiný dílec).
		/// TDocumentCls (factory knihovna) pak tyto fiktivní dílce detekuje a vkládá je rozložené.
		/// </summary>
		bool ITechnologyLibrary.AllowFictiousItem { get { return false; } }
		/// <summary>
		/// Pro daný T dokument najde hlavičková data (T modifikaci, K modifikaci a Dílec) a vloží je do dokumentu.
		/// </summary>
		/// <param name="tDocument">T dokument</param>
		/// <param name="qtyFinal">Množství finálu, které se má vyrábět</param>
		void ITechnologyLibrary.GetTDocumentData(TDocumentCls tDocument, decimal qtyFinal)
		{
			ProductOrderCls productData = FindProductOrder(tDocument.DocumentKey, NotFindMode.GetFromDbNull);
			if (productData == null) return;
			tDocument.AOperationModKey = productData.AOperationMod;
			tDocument.ConstrElementKey = productData.ConstrElement;
			tDocument.QtyFinal = productData.Qty;
			tDocument.QtyRequired = (productData.QtyAxis);
			tDocument.FictiousItemQtyToParallel = TFictiousItemQtyToParallelMode.NoChange;
			tDocument.IsFixed = false;
			tDocument.FixedTime = TimeRange.Empty;
		}
		/// <summary>
		/// Metoda najde a vrátí režim převodu množství jednotkového z komponenty, kde je fiktivní dílec, na operace tohoto fiktivního dílce.
		/// </summary>
		/// <param name="tDocumentKey"></param>
		/// <returns></returns>
		TFictiousItemQtyToParallelMode ITechnologyLibrary.GetFictItemQtyToParallel(int tDocumentKey)
		{
			return TFictiousItemQtyToParallelMode.NoChange;        // Výrobní příkazy nemají fiktivní dílce.
		}
		/// <summary>
		/// Pro daný T dokument vrátí jeho záznam (větu z databáze), obsahuje jak identifikaci záznamu programovou (IRecordKey), tak referenci a název.
		/// </summary>
		/// <param name="tDocumentKey">Klíč T dokumentu</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		RecordCls ITechnologyLibrary.GetTDocumentRecord(int tDocumentKey, NotFindMode notFindMode)
		{
			ProductOrderCls productData = FindProductOrder(tDocumentKey, notFindMode);
			return productData;
		}
		/// <summary>
		/// Vrátí data jedné operace, tak jako je vrací metoda GetOperationDataList() - ale zde se vrací jen jedna operace, ne celý seznam.
		/// Operace obsahuje všechny komponenty a zdroje, a přepočítané množství.
		/// </summary>
		/// <param name="tDocumentKey">Číslo T dokumentu (podle jeho typu = podle typu knihovny: STPV=T mod, VP=číslo subjektu VP)</param>
		/// <param name="qtyFinal">Množství finálu, které se má vyrábět</param>
		/// <param name="operation">Číslo operace (podle implementace: VTPV/STPV/VP)</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		/// <returns>Seznam objektů, které jsou operacemi dané dokumentace</returns>
		IOperationData ITechnologyLibrary.GetOperationData(int tDocumentKey, int operation, decimal qtyFinal, NotFindMode notFindMode)
		{
			ProductOrderOperationCls dataOp = this.FindProductOrderOperation(operation, notFindMode);
			return _GetOperationData(dataOp, qtyFinal, notFindMode);
		}
		/// <summary>
		/// Vrátí seznam operací 
		/// </summary>
		/// <param name="tDocumentKey">Číslo T dokumentu (podle jeho typu = podle typu knihovny: STPV=T mod, VP=číslo subjektu VP)</param>
		/// <param name="sortOrder">Směr třídění operací</param>
		/// <param name="qtyFinal">Množství finálu, které se má vyrábět</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		/// <returns>Seznam objektů, které jsou operacemi dané dokumentace</returns>
		List<IOperationData> ITechnologyLibrary.GetOperationDataList(int tDocumentKey, TimeRange.TimeDirection sortOrder, decimal qtyFinal, NotFindMode notFindMode)
		{
			List<IOperationData> result = new List<IOperationData>();
			List<ProductOrderOperationCls> operList = this.FindOperationListForProductOrder(tDocumentKey, sortOrder);
			foreach (ProductOrderOperationCls dataOp in operList)
			{
				result.Add(_GetOperationData(dataOp, qtyFinal, notFindMode));
			}
			return result;
		}
		/// <summary>
		/// Vrátí kompletní data jedné operace.
		/// </summary>
		/// <param name="tDocumentKey">Číslo T dokumentu (podle jeho typu = podle typu knihovny: STPV=T mod, VP=číslo subjektu VP)</param>
		/// <param name="qtyFinal">Množství finálu, které se má vyrábět</param>
		/// <param name="operation">Číslo operace (podle implementace: VTPV/STPV/VP)</param>
		/// <returns>Objekt požadované operace dané dokumentace</returns>
		private IOperationData _GetOperationData(ProductOrderOperationCls dataOp, decimal qtyFinal, NotFindMode notFindMode)
		{
			StandardMfrOperationsCls dataSt = this.FindStandardMfrOperations(dataOp.StandardMfrOperation);
            ProdOperationCls tOperation = new ProdOperationCls(dataOp, dataSt, dataOp.Qty /*qtyFinal*/, dataOp.QtyRequired);
			return tOperation;
		}
		/// <summary>
		/// Vrátí seznam komponent pro danou operaci
		/// </summary>
		/// <param name="operation">Číslo operace (podle implementace: VTPV/STPV/VP)</param>
		/// <param name="qtyFinal">Množství finálu, na které se má množství komponenty přepočítat</param>
		/// <returns>Seznam objektů, které jsou komponentami dané operace</returns>
		List<IStructureData> ITechnologyLibrary.GetStructureDataList(int operation, decimal qtyFinal)
		{
			List<IStructureData> result = new List<IStructureData>();
			List<ProductOrderStructureCls> structList = this.FindProductOrderStructureListForPoOper(operation);
			foreach (ProductOrderStructureCls dataS in structList)
			{
				if (dataS.Status == "A") continue;               // Komponenty, které jsou ve stavu A, nebudeme zohledňovat.
				decimal qtyRequired = dataS.Qty - (dataS.QtyReal + dataS.QtyBooked);
				result.Add(new ProdStructureCls(dataS, qtyFinal, qtyRequired));
			}
			return result;
		}
		/// <summary>
		/// Vrátí seznam zdrojů pro danou operaci
		/// </summary>
		/// <param name="operation">Číslo operace (podle implementace: VTPV/STPV/VP)</param>
		/// <param name="qtyFinal">Množství finálu, na které se má množství zdrojů přepočítat</param>
		/// <returns>Seznam objektů, které jsou zdrojem dané operace</returns>
		List<IResourceData> ITechnologyLibrary.GetResourceDataList(int operation, decimal qtyFinal)
		{
			List<IResourceData> result = new List<IResourceData>();
			List<ProductOrderResourceAllocCls> resourceList = this.FindProductOrderResourceListForPoOper(operation);
			foreach (ProductOrderResourceAllocCls data in resourceList)
			{
				decimal qtyRequired = qtyFinal;
				result.Add(new ProdResourceCls(data, qtyFinal, qtyRequired));
			}
			return result;
		}
		/// <summary>
		/// Vrátí záznam jedné konkrétní operace
		/// </summary>
		/// <param name="operation">Číslo operace (podle implementace: VTPV/STPV/VP)</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		/// <returns>Data operace</returns>
		IOperationData ITechnologyLibrary.GetOperationItem(int operation, NotFindMode notFindMode)
		{
			ProductOrderOperationCls dataOp = this.FindProductOrderOperation(operation, notFindMode);
			StandardMfrOperationsCls dataSt = this.FindStandardMfrOperations(dataOp.StandardMfrOperation);
			return new ProdOperationCls(dataOp, dataSt, 0m, 0m);
		}
		/// <summary>
		/// Vrátí záznam jedné konkrétní komponenty
		/// </summary>
		/// <param name="operation">Číslo operace (podle implementace: VTPV/STPV/VP)</param>
		/// <param name="structure">Číslo komponenty (podle implementace: VTPV/STPV/VP)</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		/// <returns>Data komponenty</returns>
		IStructureData ITechnologyLibrary.GetStructureItem(int operation, int structure, NotFindMode notFindMode)
		{
			ProductOrderStructureCls dataS = this.FindProductOrderStructure(structure, notFindMode);
			return new ProdStructureCls(dataS, 0m, 0m);
		}
		/// <summary>
		/// Vrátí záznam jednoho konkrétního zdroje operace
		/// </summary>
		/// <param name="resource">Číslo zdroje operace (podle implementace: VTPV/STPV/VP)</param>
		/// <param name="notFindMode">Jak se má metoda zachovat, když požadovaný záznam není v hlavní paměti (která má obsahovat všechny vyráběné prvky)</param>
		/// <returns>Data zdroje operace</returns>
		IResourceData ITechnologyLibrary.GetResourceItem(int resource, NotFindMode notFindMode)
		{
			ProductOrderResourceAllocCls data = this.FindProductOrderResource(resource, notFindMode);
			return new ProdResourceCls(data, 0m, 0m);
		}
		#endregion
		#region UKLÁDÁNÍ DAT
		/// <summary>
		/// Metoda zajistí uložení dat tohoto modulu do databáze.
		/// Data o výrobě: ukládají se jen změny v termínech a změny fixace do hlaviček výrobních příkazů a do operací.
		/// A to jen v případě, že nyní je aktivní verze plánu, označená jako "Hlavní".
		/// </summary>
		internal void SaveData()
		{
            if (!this.ParentPlanning.PlanningParameters.SaveProductDataChanges) return;

            // Toto jsou záznamy osy S = výdej do komponenty reálného VP, kde ale není na VP dokumentace, a tak se komponenty přebírají z STPV, proto nepatří do indexu :
            // List<MaterialPlanAxisItemCls> error = this.ParentPlanning.AxisHeap.AxisS.Values.Where(a => a.ProductOrderStructure == 0 && a.ChangeType == PlanSAxisChangeType.DecrementByRealComponent).ToList();

            // Připravím si index záznamů osy S, podle klíče komponenty VP:
            IndexUnique<int, MaterialPlanAxisItemCls> axisSPosIdx = new IndexUnique<int, MaterialPlanAxisItemCls>(
                a => a.ProductOrderStructure,
                this.ParentPlanning.AxisHeap.AxisS.Values.Where(a => a.ProductOrderStructure != 0 && a.ChangeType == PlanSAxisChangeType.DecrementByRealComponent));

			// Projdu výrobní příkazy, najdu k nim jejich záznamy osy S, a opíšu termíny z osy na hlavičku VP:
            using (var scope = Steward.TraceScopeBegin(Constants.TraceTypeProduct, "Save.ProductData", Constants.TraceKeyWordAplScheduler, "ProductOrder.Count = " + this._ProductOrderDict.Count.ToString()))
            {
                scope.SuppressInnerTrace(null, null, "SQL");
                foreach (ProductOrderCls productOrder in this._ProductOrderDict.Values)
                {
                    // Dohledám záznam osy S pro příjem z tohoto VP:
                    MaterialPlanAxisItemCls axis = this.ParentPlanning.AxisHeap.FindAxisSItemForSource(productOrder.GId);
                    if (axis == null) continue;

                    // DAJ 0043940: pokud v čase ukládání dat neexistuje záznam Výrobního příkazu, změníme charakter záznamu osy:
                    if (productOrder.RecordNowExists())
                        this._SaveProductOrder(productOrder, axis, axisSPosIdx);
                    else
                        this._ChangeProductOrder(productOrder, axis, axisSPosIdx);
                        
                }
            }
		}
        /// <summary>
        /// Uloží data výrobního příkazu + operací + komponent
        /// </summary>
        /// <param name="productOrder"></param>
        /// <param name="axis"></param>
        private void _SaveProductOrder(ProductOrderCls productOrder, MaterialPlanAxisItemCls axis, IndexUnique<int, MaterialPlanAxisItemCls> axisSPosIdx)
        {
            // Uložíme data výrobního příkazu:
            productOrder.SavePlanningData(axis);

            // Najdu všechny operace výrobního příkazu:
            List<ProductOrderOperationCls> operList = this.FindOperationListForProductOrder(productOrder.RecordNumber, TimeRange.TimeDirection.ToFuture);
            if (operList != null)
            {
                foreach (ProductOrderOperationCls operation in operList)
                {
                    CapacityPlanWorkItemCls task = this.ParentPlanning.AxisHeap.FindTaskCForAxisOper(axis.AxisID, operation.RecordNumber);
                    if (task != null)
                    {
                        operation.SavePlanningData(task);

                        // Najdu komponenty této operace a uložím do nich jejich datum plánovaného vydání, pokud jsou zapotřebí:
                        List<ProductOrderStructureCls> structureList = this.FindProductOrderStructureListForPoOper(operation.RecordNumber);
                        if (structureList != null)
                        {
                            foreach (ProductOrderStructureCls structure in structureList)
                            {
                                MaterialPlanAxisItemCls structAxis;
                                if (axisSPosIdx.TryGetValue(structure.RecordNumber, out structAxis))
                                {
                                    structure.SavePlanningData(structAxis);
                                }
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Uloží data na osu S, do tasků a do podřízených záznamů v případě, kdy v době tvorby plánu byl smazán výrobní příkaz
        /// </summary>
        /// <param name="productOrder"></param>
        /// <param name="axis"></param>
        /// <param name="axisSPosIdx"></param>
        private void _ChangeProductOrder(ProductOrderCls productOrder, MaterialPlanAxisItemCls axis, IndexUnique<int, MaterialPlanAxisItemCls> axisSPosIdx)
        {
            // Změníme charakter osy S o příjmu z VP na Návrh na příjem:
            axis.ChangeToProposal(true);

            // Najdu všechny operace výrobního příkazu, a změním Tasky na Návrh:
            List<ProductOrderOperationCls> operList = this.FindOperationListForProductOrder(productOrder.RecordNumber, TimeRange.TimeDirection.ToFuture);
            if (operList != null)
            {
                foreach (ProductOrderOperationCls operation in operList)
                {
                    CapacityPlanWorkItemCls task = this.ParentPlanning.AxisHeap.FindTaskCForAxisOper(axis.AxisID, operation.RecordNumber);
                    if (task != null)
                    {
                        task.ChangeToProposal(operation.AOperation, true);

                        // Najdu komponenty této operace a změním je na Návrh:
                        List<ProductOrderStructureCls> structureList = this.FindProductOrderStructureListForPoOper(operation.RecordNumber);
                        if (structureList != null)
                        {
                            foreach (ProductOrderStructureCls structure in structureList)
                            {
                                MaterialPlanAxisItemCls structAxis;
                                if (axisSPosIdx.TryGetValue(structure.RecordNumber, out structAxis))
                                {
                                    structAxis.ChangeToProposal(true);
                                }
                            }
                        }
                    }
                }
            }
        }
        #endregion
        #region VYHLEDÁNÍ TEXTU GetReferNazev()
        /// <summary>
        /// Vrátí Refer: Nazev pro daný záznam.
        /// Pokud neexistuje, vrátí "".
        /// </summary>
        /// <param name="recordGId"></param>
        /// <returns></returns>
        public bool ContainReferNazev(int classNumber)
        {
            System.Collections.IDictionary dict = this._GetDictionaryForClass(classNumber);
            return (dict != null);
        }
        /// <summary>
        /// Vrátí Refer: Nazev pro daný záznam.
        /// Pokud neexistuje, vrátí "".
        /// </summary>
        /// <param name="recordGId"></param>
        /// <returns></returns>
        public string GetReferNazev(GID recordGId)
        {
            return this.GetReferNazev(recordGId.ClassNumber, recordGId.RecordNumber);
        }
        /// <summary>
        /// Vrátí Refer: Nazev pro daný záznam.
        /// Pokud neexistuje, vrátí "".
        /// </summary>
        /// <param name="recordGId"></param>
        /// <returns></returns>
        public string GetReferNazev(int classNumber, int recordNumber)
        {
            System.Collections.IDictionary dict = this._GetDictionaryForClass(classNumber);
            if (dict == null) return "";
            if (!dict.Contains(recordNumber))
            {
                dict = this._GetDictionaryDbForClass(classNumber);
                if (!dict.Contains(recordNumber))
                    return "";
            }
            object value = dict[recordNumber];
            IReferNazev record = value as IReferNazev;
            if (record == null) return "";
            return record.ReferNazev;
        }
        /// <summary>
        /// Najde a vrátí Dictionary pro dané číslo třídy
        /// </summary>
        /// <param name="classNumber"></param>
        /// <returns></returns>
        private System.Collections.IDictionary _GetDictionaryForClass(int classNumber)
        {
            switch (classNumber)
            {
                case ProductOrderCls.ClassNr:
                    return this._ProductOrderDict;
                case ProductOrderOperationCls.ClassNr:
                    return this._ProductOrderOperationDict;
                case ProductOrderStructureCls.ClassNr:
                    return this._ProductOrderStructureDict;
                case ProductOrderResourceAllocCls.ClassNr:
                    return this._ProductOrderResourceAllocDict;
                case StandardMfrOperationsCls.ClassNr:
                    return this._StandardMfrOperationsDict;
            }
            return null;
        }
        /// <summary>
        /// Najde a vrátí Dictionary pro dané číslo třídy, v režimu DB
        /// </summary>
        /// <param name="classNumber"></param>
        /// <returns></returns>
        private System.Collections.IDictionary _GetDictionaryDbForClass(int classNumber)
        {
            switch (classNumber)
            {
                case ProductOrderCls.ClassNr:
                    return this._ProductOrderDbDict;
                case ProductOrderOperationCls.ClassNr:
                    return this._ProductOrderOperationDbDict;
                case ProductOrderStructureCls.ClassNr:
                    return this._ProductOrderStructureDbDict;
                case ProductOrderResourceAllocCls.ClassNr:
                    return this._ProductOrderResourceAllocDbDict;
            }
            return null;
        }
        #endregion
    }
	#region CLASS ProductDataStatusCls : sbírka stavových proměnných modulu ProductData
	/// <summary>
	/// ProductDataStatusCls : sbírka stavových proměnných modulu ProductData
	/// </summary>
	internal class ProductDataStatusCls
	{
		/// <summary>
		/// Skladové doklady jsou načteny z databáze do paměti
		/// </summary>
		internal bool WhDocItemLoaded { get; set; }
		/// <summary>
		/// Příznak pozastaveného třídění časových položek na skladových kartách. 
		/// Po dobu masivního přidávání dat je online třídění pozastavené.
		/// </summary>
		internal bool SuspendSorting { get; set; }
	}
	#endregion
    #region KONVERZE DATOVÝCH TŘÍD, KTERÉ ZAJISTÍ IMPLEMENTACI SPOLEČNÝCH INTERFACE - ŘEŠENO JAKO NOVÉ TŘÍDY, KTERÉ HOSTÍ ZÁKLADNÍ DATOVÉ TŘÍDY
    /// <summary>
	/// ProdOperationCls : IOperationData ( hostitel pro instanci ProductOrderOperationCls )
	/// Záznam této třídy se vytváří vždy nový, při každém požadavku na zpřístupnění T dokumentace.
	/// Vytváří se na základě dat operace VP, tvoří můstek mezi operací VP a interface IOperationData.
	/// </summary>
	internal class ProdOperationCls : IOperationData
	{
		#region KONSTRUKCE
		internal ProdOperationCls(ProductOrderOperationCls dataOp, StandardMfrOperationsCls dataSt, decimal qtyFinal, decimal qtyRequired)
		{
			this._DataOp = dataOp;
			this._DataSt = dataSt;
			this._ParalelPassingCount = dataOp.ParalelPassingCount; // Tuto hodnotu chce interface editovat, ale nechceme ji propisovat do operace VP.
			this._UnitCount = dataOp.UnitCount;                     //   dtto.
			this._QtyFinal = qtyFinal;
			this._QtyRequired = qtyRequired;
			this._StructureList = new List<IStructureData>();
			this._ResourceList = new List<IResourceData>();
		}
		private ProductOrderOperationCls _DataOp;
		private StandardMfrOperationsCls _DataSt;
		private decimal _ParalelPassingCount;
		private decimal _UnitCount;
		private decimal _QtyFinal;
		private decimal _QtyRequired;
		private List<IStructureData> _StructureList;
		private List<IResourceData> _ResourceList;
		#endregion
		#region IOperationData & IOperationTime Members
		int IOperationData.Operation { get { return this._DataOp.RecordNumber;} }
		TDocumentType IOperationData.DocumentType { get { return TDocumentType.Production; } }
        MfrOperationPlanning IOperationData.MfrOpPlanning { get { return this._DataOp.MfrOpPlanning ; } }
		string IOperationData.Refer { get { return this._DataOp.Line.ToString(); } }
		string IOperationData.Nazev { get { return this._DataOp.OpName; } }
		decimal IOperationData.QtyFinal { get { return this._QtyFinal; } }
		decimal IOperationData.QtyRequired { get { return this._QtyRequired; } }
		Color IOperationData.OperationColor { get { return Color.Empty; } }

		decimal IOperationTime.MoveTime { get { return this._DataOp.MoveTime; } }
		int IOperationTime.MoveTimeMu { get { return this._DataOp.MoveTimeMu; } }
		decimal IOperationTime.Tbc { get { return this._DataOp.Tbc; } }
		int IOperationTime.TbcMu { get { return this._DataOp.TbcMu; } }
		decimal IOperationTime.Tac { get { return this._DataOp.Tac; } }
		int IOperationTime.TacMu { get { return this._DataOp.TacMu; } }
		bool IOperationTime.TacDiv { get { return true; } }             // DAJ: po dohodě s Jirkou Hrubým tvrdíme, že čas TAC je vždy dělitelný podle potřeby
		decimal IOperationTime.Tec { get { return this._DataOp.Tec; } }
		int IOperationTime.TecMu { get { return this._DataOp.TecMu; } }
		decimal IOperationTime.UnitCount { get { return this._UnitCount; } }
		decimal IOperationTime.ParalelPassingCount { get { return this._ParalelPassingCount; } set { this._ParalelPassingCount = value; } }
		decimal IOperationTime.MoveBatchQty { get { return this._DataOp.MoveBatchQty; } }
		decimal IOperationTime.QtyFinal { get { return this._QtyFinal; } }
		decimal IOperationTime.QtyRequired { get { return this._QtyRequired; } }

		int IOperationData.StandardMfrOperation { get { return this._DataOp.StandardMfrOperation; } }
		int IOperationData.Workplace { get { return this._DataOp.Workplace; } }
		decimal IOperationData.WorkersQty { get { return this._DataOp.WorkersQty; } }
		SqlInt32 IOperationData.Profession { get { return this._DataSt.Profession; } }
		decimal IOperationData.UnitCount { get { return this._UnitCount; } set { this._UnitCount = value; } }
		TOperationUnitCountVariability IOperationData.UnitCountVariable { get { return TOperationUnitCountVariability.Fixed; } }   // Zde bude zásah, až se rozšíří Noris
		bool IOperationData.IsFixed { get { return this._DataOp.IsFixedOperation; } }
		TimeRange IOperationData.FixedTime { get { return (this._DataOp.IsFixedOperation ? new TimeRange(this._DataOp.DateStartPlan, this._DataOp.DateEndPlan) : TimeRange.Empty); } }
		List<IStructureData> IOperationData.StructureList{ get { return _StructureList; } }
		List<IResourceData> IOperationData.ResourceList { get { return _ResourceList; } }
		#endregion
        #region IRecordKey Members
        int IRecordKey.ClassNumber { get { return ProductOrderOperationCls.ClassNr; } }
        int IRecordKey.RecordNumber { get { return this._DataOp.RecordNumber; } }
        #endregion
    }
	// ProdStructureCls : IStructureData ( hostitel pro instanci ProductOrderStructureCls )
	internal class ProdStructureCls : IStructureData
	{
		#region KONSTRUKCE
		internal ProdStructureCls(ProductOrderStructureCls dataS, decimal qtyFinal, decimal qtyRequired)
		{
			this._DataS = dataS;
			this._QtyFinal = qtyFinal;
			this._QtyRequired = qtyRequired;
		}
		private ProductOrderStructureCls _DataS;
		private decimal _QtyFinal;
		private decimal _QtyRequired;
		#endregion
		#region IStructureData Members
		int IStructureData.Structure { get { return this._DataS.RecordNumber; } }
		int IStructureData.Operation { get { return this._DataS.OperationPo; } }
		int IStructureData.Line { get { return this._DataS.Line; } }
		TDocumentType IStructureData.DocumentType { get { return TDocumentType.Production; } }
		SqlInt32 IStructureData.Source { get { return this._DataS.Source; } }
		SqlInt32 IStructureData.Variation { get { return this._DataS.Variation; } }
		SqlInt32 IStructureData.OrderRelation { get { return this._DataS.OrderRelation; } }
		int IStructureData.PlanUnitSComp { get { return this._DataS.PlanUnitS; } }
		int IStructureData.Stock { get { return this._DataS.Stock; } }
		decimal IStructureData.QtyRequired { get { return this._QtyRequired; } }
		decimal IStructureData.QtyFinal { get { return this._QtyFinal; } }
		decimal IStructureData.QtyOne { get { return (this._DataS.QtyU.IsNull ? this._DataS.QtyNetU : this._DataS.QtyU.Value); } }
		bool IStructureData.FictiousItem { get { return (this._DataS.FictiousItem == "A"); } }
		bool IStructureData.ByProduct { get { return (this._DataS.ByProduct == "A"); } }
		#endregion
        #region IRecordKey Members
        int IRecordKey.ClassNumber { get { return ProductOrderStructureCls.ClassNr; } }
        int IRecordKey.RecordNumber { get { return this._DataS.RecordNumber; } }
        #endregion
    }
	// ProdResourceCls : IResourceData ( hostitel pro instanci ProductOrderResourceAllocCls )
	internal class ProdResourceCls : IResourceData
	{
		#region KONSTRUKCE
		internal ProdResourceCls(ProductOrderResourceAllocCls data, decimal qtyFinal, decimal qtyRequired)
		{
			this._Data = data;
			this._QtyFinal = qtyFinal;
			this._QtyRequired = qtyRequired;
		}
		private ProductOrderResourceAllocCls _Data;
		private decimal _QtyFinal;
		private decimal _QtyRequired;
		#endregion
		#region IResourceData Members
		int IResourceData.Operation { get { return this._Data.ProductOrderOperation; } }
		TDocumentType IResourceData.DocumentType { get { return TDocumentType.Production; } }
		decimal IResourceData.Qty { get { return this._Data.Qty; } }
		TResourceUnitQty IResourceData.UnitQty { get { return TResourceUnitQty.QtyToUnitCount; } }     // Zde bude zásah, až se rozšíří Noris
		int IResourceData.Source { get { return this._Data.Source; } }
		bool IResourceData.CalcTimeTac { get { return (this._Data.CalcTimeTac == "A"); } }
		bool IResourceData.CalcTimeTbc { get { return (this._Data.CalcTimeTbc == "A"); } }
		bool IResourceData.CalcTimeTec { get { return (this._Data.CalcTimeTec == "A"); } }
		#endregion
	}
	#endregion
}
namespace Noris.Schedule.Planning.DataFace
{
	#region EXTENDERY DATOVÝCH TŘÍD (DATAFACE), FORMOU partial class, UMOŽŇUJÍCÍ TVORBU NOVÝCH ZÁZNAMŮ NA ZÁKLADĚ EXPLICITNĚ PŘEDANÝCH ÚDAJŮ
	// Extender třídy ProductOrderCls, rozšiřující chování třídy o aplikační logiku
	public partial class ProductOrderCls : RecordCls, Noris.Schedule.Support.Sql.IDbRecord
	{
		#region PŘIDANÉ PROPERTY
		/// <summary>
		/// Množství na příjemkách včetně vyřazení (QtyReal + QtyReject)
		/// </summary>
		public decimal QtyReceipt { get { return this.QtyReal + this.QtyReject; } }
		/// <summary>
        /// Množství dosud nepřijaté (může být záporné, při překročení příjemky) (Qty - QtyReceipt), kde QtyReceipt = (QtyReal + QtyReject)
		/// </summary>
		public decimal QtyRemaining { get { return this.Qty - this.QtyReceipt; } }
		/// <summary>
        /// Množství na osu S jako očekávané čisté množství k příjmu, nezáporné (Productorder.Qty - QtyReceipt), kde QtyReceipt = (QtyReal + QtyReject)
		/// </summary>
        public decimal QtyAxis { get { return Math.Max(this.Qty - this.QtyReceipt, 0M); } }
        /// <summary>
        /// Množství na osu S jako očekávané zbývající množství se ztrátou = nezáporné, (Productorder.QtySource - QtyReceipt), kde QtyReceipt = (QtyReal + QtyReject)
        /// </summary>
        public decimal QtyAxisWithLoss { get { return Math.Max((this.QtySource - this.QtyReceipt), 0M); } }
		/// <summary>
		/// Fixovaný výrobní příkaz, typová hodnota. Enum používá shodný s ProductOrderOperationCls.MfrFixedOperation
		/// </summary>
		public MfrFixedOperationType MfrFixedProductOrder
		{
			get { return PlanningComponentCls.SqlInt16ToMfrFixedOperation(this.FixedProductOrder); }
			set { this.FixedProductOrder = PlanningComponentCls.MfrFixedOperationToSqlInt16(value); }
		}
		#endregion
		#region Rozšířené načítání dat (přidané sloupce z DB nad rámec základní třídy)
        /// <summary>
        /// SQL SELECT pro načtení columnů, odpovídajících této struktuře. Pozor: neobsahuje filtr ani order.
        /// Rozšířená varianta, načítá i příznak existence operací VP (product_order_operation, ProductOrderOperationExists).
        /// </summary>
        public static string SqlSelectExt
        {
            get
            {
                string sql = SqlSelect;
                int index1 = sql.IndexOf("SELECT ");
                int index2 = sql.LastIndexOf("FROM ");
                string[] fields = sql.Substring(index1 + 7, index2 - index1 - 7).Split(',');
                string result = "SELECT ";
                for (int f = 0; f < fields.Length; f++)
                    result += (f == 0 ? "" : ", ") + "po." + fields[f].Trim();
                result += @", isnull(poo.product_order,0) as product_order_operation_exists 
 FROM lcs.product_order po
 LEFT OUTER JOIN (select distinct product_order from lcs.product_order_operation) as poo on poo.product_order = po.cislo_subjektu";
                return result;
            }
        }
        /// <summary>
        /// Jméno sloupce, který obsahuje klíč záznamu, určeno pro filtry.
        /// Rozšířená varianta, s aliasem po. pro používání se selectem SqlSelectExt.
        /// </summary>
        public static new string SqlRecordExt { get { return "po.cislo_subjektu"; } }
        /// <summary>
        /// Příznak, že pro tento výrobní příkaz existuje alespoň jedna operace VP, v libovolném stavu
        /// </summary>
        public bool ProductOrderOperationExists { get { return (this._product_order_operation_exists != 0); } }
        private int _product_order_operation_exists;
        /// <summary>
        /// Načtení dat z readeru v rámci extenderu třídy.
        /// Základní fields načítá základní metoda třídy, ale fields rozšířené (nad rámec třídy, ale načítané z databáze) načítá extender v této metodě.
        /// Důvodem je to, aby se strojově generovaný kód (typicky v DataFaces) třídy nemusel ručně modifikovat.
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="map"></param>
        public override void FillFromReaderExtended(SqlDataReader reader, FieldMapperLoad map)
        {
            _product_order_operation_exists = (int)map.FillDataIntoField<int>("product_order_operation_exists", reader);
        }
        #endregion
		#region UKLÁDÁNÍ DAT
		/// <summary>
		/// Metoda zajistí uložení dat plánování z plánovací osy do výrobního příkazu.
		/// Ukládají se data: DateStartPlan, DateEndPlan, MfrFixedProductOrder
		/// </summary>
        internal void SavePlanningData(MaterialPlanAxisItemCls axis)
        {
            // Následující řádek je nadbytečný, protože existence věty se testuje před voláním této metody (při neexistenci se volá jiná metoda).
            // Navíc by vedl k opakovanému dotazu do DB, a tedy ke zbytečnému zdržení.
            // if (!this.RecordNowExists()) return;              // pokud záznam neexistuje, skončíme. Na úrovni výrobního příkazu je ošetřeno navíc tak, že pokud záznam neexistuje, převede se osa S z "Příjem z VP" na "Návrh na příjem z VP".

            // Záznam (výrobní příkaz) existuje:
            if (axis.DatePlanBegin.Year > 1900)
                this.DateStartPlan = axis.DatePlanBegin;
            if (axis.DatePlanEnd.Year > 1900)
                this.DateEndPlan = axis.DatePlanEnd;
            this.MfrFixedProductOrder = PlanningComponentCls.IsFixedToMfrFixed(axis.IsFixedAxis);   // bool to MfrFixedOperationType, a pak to set převede na SqlInt16

            this.UpdateColumns("date_start_plan,date_end_plan,fixed_product_order");

        }
		#endregion
	}
	//  Extender třídy ProductOrderOperationCls, přidává property MoveTimeMu a MfrFixedOperation
	public partial class ProductOrderOperationCls : RecordCls, Noris.Schedule.Support.Sql.IDbRecord
	{
		#region PŘIDANÉ PROPERTY
        ///<summary>Množství požadované do plánování kapacit výroby. Obsahuje (Qty - (QtyReal + QtyReject)), pouze pokud stav operace je "N", jinak obsahuje 0.</summary>
        public decimal QtyRequired { get { return (this.Status == "N" ? this.Qty - (this.QtyReal + this.QtyReject) : 0m); } }
		///<summary>Atribut: MJ MoveTime (v datové struktuře není, vrací se standardně 1 hodina</summary>
		public Int16 MoveTimeMu { get { return 3600; } }
		/// <summary>
		/// Fixovaná operace, typová hodnota. Enum používá shodný s ProductOrderCls.MfrFixedProductOrder
		/// </summary>
		public MfrFixedOperationType MfrFixedOperation
		{
			get { return PlanningComponentCls.SqlInt16ToMfrFixedOperation(this.FixedOperation); }
			set { this.FixedOperation = PlanningComponentCls.MfrFixedOperationToSqlInt16(value); }
		}
		/// <summary> { get; }
		/// Je tato operace nějak fixovaná ? (FixedTime or FixedTimeAndUnit)
		/// </summary>
		public bool IsFixedOperation
		{
			get
			{
				MfrFixedOperationType mfrFixed = this.MfrFixedOperation;
				return (mfrFixed == MfrFixedOperationType.Fixed);
			}
		}
        /// <summary>
        /// Plánovat tuto operaci, a jak?
        /// </summary>
        public MfrOperationPlanning MfrOpPlanning
        {
            get { return EnumConvert.DbToType(this.OperationPlanning, MfrOperationPlanning.PlanByProcess); }
            set { this.OperationPlanning = EnumConvert.TypeToDb(value); }
        }
		#endregion
		#region UKLÁDÁNÍ DAT
		/// <summary>
		/// Metoda zajistí uložení dat plánování z kapacitního úkolu do operace výrobního příkazu.
		/// Ukládají se data: DateStartPlan, DateEndPlan, MfrFixedOperation
		/// </summary>
		internal void SavePlanningData(CapacityPlanWorkItemCls task)
		{
            if (!this.RecordNowExists()) return;              // pokud záznam neexistuje, skončíme.

            TimeRange taskTime = task.TimeWork;

			if (taskTime.Begin.Year > 1900)
				this.DateStartPlan = taskTime.Begin;
			if (taskTime.End.Year > 1900)
				this.DateEndPlan = taskTime.End;
			this.MfrFixedOperation = PlanningComponentCls.IsFixedToMfrFixed(task.IsFixedTask);
            this.Link = task.LinkRecordNumber;
            // this.ParalelPassingCount = task.WorkPassList.Count;               // DAJ 0043680 zrušeno

            // this.UpdateColumns("date_start_plan,date_end_plan,fixed_operation,link,paralel_passing_count");
            this.UpdateColumns("date_start_plan,date_end_plan,fixed_operation"); // DAJ 0043680 zrušeno:  paralel_passing_count
		}
		#endregion
        #region Rozšíření Operace VP o data kapacitního úkolu, která zajistí přenesení detailů plánu u fixovaných operací do dalšího zpracování plánu (průchody, množství, časy, KPJ)
        /// <summary>
        /// Soupis dat z jednotlivých paralelních průchodů, jak byly připraveny v minulé verzi plánu.
        /// V tomto seznamu tato data "přežijí" čas maelstromu, kdy je jeden plán smazán a začíná se vytvářet plán další,
        /// a tato operace je "fixovaná" = uživatel očekává, že zůstane zachována beze změn.
        /// </summary>
        public List<PlanningParallelPassCls> ParallelPassList { get; set; }
        #endregion
    }
    //  Extender třídy ProductOrderStructureCls, přidává metodu SavePlanningData()
    public partial class ProductOrderStructureCls : RecordCls, Noris.Schedule.Support.Sql.IDbRecord
    {
        #region UKLÁDÁNÍ DAT
        /// <summary>
        /// Metoda zajistí uložení dat plánování z osy o výdeji komponenty do záznamu komponenty výrobního příkazu.
        /// Ukládají se data: DateStartPlan, DateEndPlan, MfrFixedOperation
        /// </summary>
        internal void SavePlanningData(ProcessData.MaterialPlanAxisItemCls axis)
        {
            if (!this.RecordNowExists()) return;              // pokud záznam neexistuje, skončíme.

            this._date_plan = PlanningComponentCls.DateTimeToSqlDateTime(axis.DatePlanEnd);
            this.UpdateColumns("date_plan");
        }
        #endregion
    }
	#endregion
}